#region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#endregion

using System;
using System.Collections.Generic;
using System.IO;

using Osh.Dvb;
using Osh.Epg;
using Osh.Media;

namespace Osh.ProgDvb
{
	/// <summary>
	/// Summary description for Channel.
	/// </summary>
	public class Channel : ChannelBase, IDvbChannel
	{
		public static string ChannelUriSchema = "progdvb";

		private const int ChannelIdOffset = 2;
		private const int NameOffset = ChannelIdOffset + 16;
		private const int ChannelNumberOffset = 0x34F;
		private const int DeviceIdOffset = 0x35F;
		private const int MulticastGroupIPOffset = 0x36E;
		private const int MulticastGroupPortOffset = 0x372;

		private List<IMediaFolder> _folders;
		private MediaFolderCollection _readOnlyFolders;
		private ChannelId _channelId;
		private string _serviceName;
		private string _providerName;
		private string _networkName;
		private ushort _videoId;
		private ushort _teletextId;
		private ushort _pcr;
		private ushort _pmt;
		private ushort _ecm;

		public Channel(ChannelDatabase database, BinaryReader reader)
		{
			this.Library = database;

			long position = reader.BaseStream.Position;

			ushort size = reader.ReadUInt16();

			// Read ChannelId
			byte[] channelIdBytes = new byte[ChannelId.Size];
			int offset = 0;
			reader.Read(channelIdBytes, 0, 16); offset += 16;
			if(database.SupportsDeviceId)
			{
				reader.BaseStream.Position = position + DeviceIdOffset;
				reader.Read(channelIdBytes, offset, DeviceId.Size); offset += DeviceId.Size;

				// We cannot detect the presence of a IPTV multicast address in channel ID from the database version,
				// so lets use the channel data size.
				if(size > MulticastGroupIPOffset)
				{
					reader.BaseStream.Position = position + MulticastGroupIPOffset;
					reader.Read(channelIdBytes, offset, 4); offset += 4;
				}
			}
			_channelId = new ChannelId(channelIdBytes);

			reader.BaseStream.Position = position + NameOffset;
			base.Name = reader.ReadString(64);
			_serviceName = reader.ReadString(32);
			_providerName = reader.ReadString(32);
			_networkName = reader.ReadString(32);
			
			_videoId = reader.ReadUInt16();
			_teletextId = reader.ReadUInt16();
			_pcr = reader.ReadUInt16();
			_pmt = reader.ReadUInt16();
			_ecm = reader.ReadUInt16();

			reader.BaseStream.Position = position + ChannelNumberOffset;
			this.ChannelNumber = reader.ReadInt32();
			
			reader.BaseStream.Position = position + size;

			base.Uri = Channel.ChannelIdToUri(this.Id);
		}

		public ChannelId Id
		{
			get { return _channelId; }
		}

		public string IdString
		{
			get { return _channelId.ToString(); }
		}

		public ushort TransportId
		{
			get { return _channelId.TransportId; }
		}
		public ushort NetworkId
		{
			get { return _channelId.NetworkId; }
		}
		public ushort ServiceId
		{
			get { return _channelId.ServiceId; }
		}
		public ushort SatPosition
		{
			get { return _channelId.SatPosition; }
		}
		public ushort Lnb
		{
			get { return _channelId.Lnb; }
		}
		public uint Frequency
		{
			get { return _channelId.Frequency; }
		}

		public ushort VideoId
		{
			get { return _videoId; }
		}

		public ushort TeletextId
		{
			get { return _teletextId; }
		}

		public ushort Pcr
		{
			get { return _pcr; }
		}

		public ushort Pmt
		{
			get { return _pmt; }
		}

		public ushort Ecm
		{
			get { return _ecm; }
		}

		public string ServiceName
		{
			get { return _serviceName; }
		}
		
		public string ProviderName
		{
			get { return _providerName; }
		}
		
		public string NetworkName
		{
			get { return _networkName; }
		}

		internal void AddFolder(ChannelFolder folder)
		{
			this.FoldersInternal.Add(folder);
		}

		private List<IMediaFolder> FoldersInternal
		{
			get
			{
				if(_folders == null)
					_folders = new List<IMediaFolder>();
				return _folders;
			}
		}

		public MediaFolderCollection Folders
		{
			get
			{
				if(_readOnlyFolders == null)
					_readOnlyFolders = new MediaFolderCollection(this.FoldersInternal);
				return _readOnlyFolders;
			}
		}

		public override ChannelEpg GetEpg(DateTime startTime, DateTime endTime)
		{
			IEpgProvider epgProvider = OshHostBase.Current.Services.GetService<IEpgProvider>();
			if(epgProvider == null)
				return new ChannelEpg(startTime, endTime);
			
			return epgProvider.GetChannelEpg(this, startTime, endTime);
		}

		public static Uri ChannelIdToUri(ChannelId channelId)
		{
			return new Uri(ChannelUriSchema + ":" + channelId.ToString());
		}

		public static ChannelId UriToChannelId(Uri channelUri)
		{
			if(!string.Equals(channelUri.Scheme, ChannelUriSchema, StringComparison.OrdinalIgnoreCase))
				throw new ArgumentOutOfRangeException("channelUri", channelUri, "Invalid schema of channel Uri.");

			ChannelId channelId = new ChannelId(channelUri.PathAndQuery);
			return channelId;
		}
	}
}
